<

フラグメント シェーダの作成と使用

カスタム シェーダを使用して豊かなグラフィック効果を提供できます Flutter SDK によって提供されるものを超えています。 シェーダは、小規模で作成されたプログラムです。 GLSL として知られる Dart のような言語、 ユーザーの GPU 上で実行されます。

カスタム シェーダーが Flutter プロジェクトに追加される にリストすることで、pubspec.yamlファイル、 を使用して取得され、FragmentProgramAPI。

アプリケーションへのシェーダの追加

シェーダは、GLSL ファイルの形式で、.frag拡大、 で宣言する必要がありますshadersプロジェクトのセクションpubspec.yamlファイル。 Flutter コマンドライン ツールはシェーダーをコンパイルします 適切なバックエンド形式に変換し、 そして必要な実行時メタデータを生成します。 コンパイルされたシェーダーは、アセットと同様にアプリケーションに組み込まれます。

flutter:
  shaders:
    - shaders/myshader.frag

デバッグモードで実行すると、 シェーダ プログラムへの変更により再コンパイルがトリガーされる ホット リロードまたはホット リスタート中にシェーダを更新します。

パッケージのシェーダーがプロジェクトに追加される とpackages/$pkgnameシェーダ プログラム名の先頭に付けられる (どこ$pkgnameはパッケージの名前です)。

実行時にシェーダーをロードする

シェーダをロードするにはFragmentProgram実行時のオブジェクト、 使用FragmentProgram.fromAssetコンストラクタ。 アセットの名前はシェーダーへのパスと同じです で与えられたpubspec.yamlファイル。

void loadMyShader() async {
  var program = await FragmentProgram.fromAsset('shaders/myshader.frag');
}

FragmentProgramオブジェクトを使用して作成できます 1つ以上のFragmentShaderインスタンス。 あFragmentShaderオブジェクトはフラグメントプログラムを表します 特定のセットと一緒に制服(構成パラメータ)。 使用可能なユニフォームは、シェーダの定義方法によって異なります。

void updateShader(Canvas canvas, Rect rect, FragmentProgram program) {
  var shader = program.fragmentShader();
  shader.setFloat(0, 42.0);
  canvas.drawRect(rect, Paint()..shader = shader);
}

キャンバスAPI

フラグメント シェーダはほとんどの Canvas API で使用できます 設定することによりPaint.shader。 たとえば、使用するときは、Canvas.drawRectシェーダは、四角形内のすべてのフラグメントに対して評価されます。 のような API の場合Canvas.drawPathストロークされたパスで、 シェーダは、ストロークされた線内のすべてのフラグメントに対して評価されます。 一部の API などCanvas.drawImage、シェーダの値を無視します。

void paint(Canvas canvas, Size size, FragmentShader shader) {
  // Draws a rectangle with the shader used as a color source.
  canvas.drawRect(
    Rect.fromLTWH(0, 0, size.width, size.height),
    Paint()..shader = shader,
  );

  // Draws a stroked rectangle with the shader only applied to the fragments
  // that lie within the stroke.
  canvas.drawRect(
    Rect.fromLTWH(0, 0, size.width, size.height),
    Paint()
      ..style = PaintingStyle.stroke
      ..shader = shader,
  )
}

シェーダのオーサリング

フラグメント シェーダは GLSL ソース ファイルとして作成されます。 慣例により、これらのファイルには.frag拡大。 (Flutter は頂点シェーダーをサポートしていません。 どれが持っているでしょう.vert拡大。)

460 から 100 までのすべての GLSL バージョンがサポートされています。 ただし、利用できる機能には制限があるものもあります。 このドキュメントの残りの例では、バージョンを使用します。460 core

シェーダーには次の制限があります Flutter と一緒に使用する場合:

  • UBO と SSBO はサポートされていません
  • sampler2Dサポートされている唯一のサンプラー タイプです
  • の 2 引数バージョンのみtexture(サンプラーと UV) がサポートされています
  • 追加の可変入力を宣言することはできません
  • Skia をターゲットにする場合、精度のヒントはすべて無視されます。
  • 符号なし整数とブール値はサポートされていません

制服

フラグメント プログラムは、次のように定義することで構成できます。uniformGLSL シェーダ ソースの値 そしてこれらの値を Dart に設定します 各フラグメント シェーダ インスタンス。

GLSL タイプの浮動小数点ユニフォームfloatvec2vec3、 とvec4を使用して設定されますFragmentShader.setFloat方法。 GLSL サンプラー値。sampler2Dタイプ、 を使用して設定されます3f9ベッド3d-9f0d-4da4-ba2c-521b58777858方法。

それぞれの正しいインデックスuniform値は順序によって決まります 統一値がフラグメント プログラムで定義されていること。 複数の float で構成されるデータ型の場合、vec4、 電話しなければなりませんFragmentShader.setFloat値ごとに 1 回。

たとえば、GLSL フラグメント プログラムで次のユニフォーム宣言があるとします。

uniform float uScale;
uniform sampler2D uTexture;
uniform vec2 uMagnitude;
uniform vec4 uColor;

これらを初期化するための対応する Dart コードuniform値は次のとおりです。

void updateShader(FragmentShader shader, Color color, Image image) {
  shader.setFloat(0, 23);  // uScale
  shader.setFloat(1, 114); // uMagnitude x
  shader.setFloat(2, 83);  // uMagnitude y

  // Convert color to premultiplied opacity.
  shader.setFloat(3, color.red / 255 * color.opacity);   // uColor r
  shader.setFloat(4, color.green / 255 * color.opacity); // uColor g
  shader.setFloat(5, color.blue / 255 * color.opacity);  // uColor b
  shader.setFloat(6, color.opacity);                     // uColor a

  // Initialize sampler uniform.
  shader.setImageSampler(0, image);
 }

で使用されるインデックスに注目してください。FragmentShader.setFloat数えないでくださいsampler2Dユニフォーム。 このユニフォームは別途設定されておりますFragmentShader.setImageSampler、 インデックスは 0 から始まります。

初期化されていないフロート ユニフォームはデフォルトで次のようになります。0.0

現在位置

シェーダーは、varyingのローカル座標を含む値 評価される特定のフラグメント。この機能を使用して計算します 現在の位置に依存するエフェクト。次の方法でアクセスできます。 をインポートしていますflutter/runtime_effect.glslライブラリと呼び出しFlutterFragCoord関数。例えば:

#include <flutter/runtime_effect.glsl>

void main() {
  vec2 currentPos = FlutterFragCoord().xy;
}

から返される値FlutterFragCoordとは異なりますgl_FragCoordgl_FragCoord画面空間の座標を提供します。通常は次のようになります。 バックエンド間でシェーダーの一貫性を確保するために回避されます。 Skia バックエンドをターゲットとする場合、 への呼び出しgl_FragCoordローカルにアクセスするように書き換えられます 座標を書き換えますが、Impeller ではこの書き換えは不可能です。

色の組み込みデータ型はありません。 代わりに、それらは一般的に次のように表されます。vec4各コンポーネントは RGBA の 1 つに対応します カラーチャンネル。

単一の出力fragColorカラー値が期待されます の範囲内になるように正規化されます0.01.0そして、アルファが事前に乗算されていること。 これは、使用する一般的な Flutter カラーとは異なります。 ある0-255値エンコーディングと事前乗算されていないアルファを持ちます。

サンプラー

サンプラーは、dart:ui Image物体。 この画像は、デコードされた画像から取得できます。 またはアプリケーションの一部からScene.toImageSyncまたPicture.toImageSync

#include <flutter/runtime_effect.glsl>

uniform vec2 uSize;
uniform sampler2D uTexture;

out vec4 fragColor;

void main() {
  vec2 uv = FlutterFragCoord().xy / uSize;
  fragColor = texture(uTexture, uv);
}

デフォルトでは、画像はTileMode.clamp外部の価値観を判断する の範囲の[0, 1]振る舞う。 タイルモードのカスタマイズはできません サポートされており、シェーダーでエミュレートする必要があります。

パフォーマンスに関する考慮事項

Skia バックエンドをターゲットとする場合、 シェーダのロードは高価になる可能性があります。 適切にコンパイルする必要があります 実行時のプラットフォーム固有のシェーダー。 アニメーション中に 1 つ以上のシェーダーを使用する場合は、 事前にフラグメント プログラム オブジェクトをプリキャッシュすることを検討してください。 アニメーションを開始します。

再利用できますFragmentShaderフレーム間のオブジェクト。 これは、新しいものを作成するより効率的ですFragmentShaderフレームごとに。

パフォーマンスの高いシェーダーの作成に関する詳細なガイドについては、 チェックアウト効率的なシェーダの作成GitHub 上で。

その他のリソース

詳細については、ここにいくつかのリソースがあります。

  • シェーダーの本パトリシオ・ゴンザレス・ヴィヴォ、ジェン・ロウ著
  • シェーダーおもちゃ、共同シェーダー プレイグラウンド
  • simple_shader、単純な Flutter フラグメント シェーダのサンプル プロジェクト